package il.technion.cs236369.threadPool;

import il.technion.cs236369.common.Common;
import il.technion.cs236369.interfaces.IObserver;

import java.io.PrintStream;

/**
 * A thread that continuously polls a ThreadPool for new missions
 * 
 * @author Gal Lalouche
 */
class PoolThread extends Thread implements IObserver {
	/**
	 * The thread pool to poll
	 */
	private final ThreadPool _pool;
	private final TimerThread _timer;

	/**
	 * The out to write debug messages to
	 */
	private final PrintStream _out;

	/**
	 * Creates a new pool thread
	 * 
	 * @param pool
	 *            The pool to get the queues from
	 * @param timeoutTime
	 *            The maximum amount of time to work on a task (use null for
	 *            infinite)
	 * @param name
	 *            The thread's name
	 */
	public PoolThread(ThreadPool pool, Integer timeoutTime, String name) {
		super(name);
		setDaemon(true);
		_pool = pool;
		_timer = new TimerThread(timeoutTime);
		_timer.setName(name + "'s timer");
		_timer.setObservable(this);
		_timer.start();
		_out = Common.getOut();
	}

	@Override
	public void run() {
		// main while loop
		while (true) {
			PoolTask runnable = null;

			do {
				runnable = getTask();
			} while (runnable == null);

			assert runnable != null;
			// run mission on timeout
			_out.println("Thead " + getName() + " running mission " + runnable);
			runMission(runnable);
		}
	}

	private PoolTask getTask() {
		PoolTask runnable;
		// get runnable from pool
		runnable = _pool.poll();
		if (runnable == null) {
			// no job available, sleep until available
			_out.println("Thead " + getName() + " going to sleep~");
			try {
				synchronized (_pool) {
					_pool.wait();
				}
			} catch (InterruptedException e) {
//				e.printStackTrace();
			}
			_out.println("Thead " + getName() + " waking up :>");
		}
		return runnable;
	}

	private void runMission(PoolTask runnable) {
		try {
			// start the timer
			synchronized (_timer) {
				_timer.notify();
			}
			runnable.getRunnable().run();
			_timer.interrupt();
		} catch (ThreadDeath e) {
			try {
				// ran out of time
				Common.getOut().println(
						"Thread " + getName() + " stopped on timer");
				if (runnable.getTimeoutRunnable() != null) {
					runnable.getTimeoutRunnable().run();
				}

			} catch (Exception e1) {
				e1.printStackTrace();
			}
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	// stop the current run
	@SuppressWarnings("deprecation")
	@Override
	public void notifyObserver() {
		stop();
	}
}
